import {
  WebSocketGateway,
  WebSocketServer,
  SubscribeMessage,
  OnGatewayConnection,
  OnGatewayDisconnect,
  ConnectedSocket,
  MessageBody,
} from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';
import { Logger } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';

/**
 * 扩展 Socket 类型，添加自定义数据
 */
interface AuthenticatedSocket extends Socket {
  data: {
    userId?: number;
  };
}

/**
 * JWT Payload 接口
 */
interface JwtPayload {
  sub: number;
  username: string | null;
  email: string | null;
  iat?: number;
  exp?: number;
}

/**
 * 通知推送 WebSocket Gateway
 * 用于实时推送好友申请、点赞、评论等通知
 */
@WebSocketGateway({
  cors: {
    origin: '*', // 生产环境应该配置具体域名
    credentials: true,
  },
  namespace: '/notifications',
})
export class NotificationsGateway
  implements OnGatewayConnection, OnGatewayDisconnect
{
  @WebSocketServer()
  server: Server;

  private readonly logger = new Logger(NotificationsGateway.name);

  // 存储在线用户：userId -> Socket
  private onlineUsers = new Map<number, AuthenticatedSocket>();

  constructor(private readonly jwtService: JwtService) {}

  /**
   * 客户端连接时触发
   */
  async handleConnection(client: AuthenticatedSocket) {
    try {
      // 从连接参数中获取 token
      const token =
        (client.handshake.auth?.token as string) ||
        (client.handshake.query?.token as string) ||
        (typeof client.handshake.headers?.authorization === 'string'
          ? client.handshake.headers.authorization.replace('Bearer ', '')
          : undefined);

      if (!token || typeof token !== 'string') {
        this.logger.warn(`客户端连接被拒绝：未提供 token`);
        client.disconnect();
        return;
      }

      // 验证 token
      const payload = await this.jwtService.verifyAsync<JwtPayload>(token);
      const userId = payload.sub;

      // 保存连接
      this.onlineUsers.set(userId, client);
      client.data.userId = userId;

      this.logger.log(
        `用户 ${userId} 已连接 WebSocket，当前在线用户数：${this.onlineUsers.size}`,
      );

      // 发送连接成功消息
      client.emit('connected', {
        message: '连接成功',
        userId,
        timestamp: new Date(),
      });
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : '未知错误';
      this.logger.error(`客户端连接失败：${errorMessage}`);
      client.disconnect();
    }
  }

  /**
   * 客户端断开连接时触发
   */
  handleDisconnect(client: AuthenticatedSocket) {
    const userId = client.data.userId;
    if (userId) {
      this.onlineUsers.delete(userId);
      this.logger.log(
        `用户 ${userId} 断开连接，当前在线用户数：${this.onlineUsers.size}`,
      );
    }
  }

  /**
   * 客户端 ping，用于保持连接
   */
  @SubscribeMessage('ping')
  handlePing(@ConnectedSocket() client: AuthenticatedSocket): void {
    client.emit('pong', { timestamp: new Date() });
  }

  /**
   * 处理用户正在输入事件
   */
  @SubscribeMessage('typing')
  handleTyping(
    @ConnectedSocket() client: AuthenticatedSocket,
    @MessageBody() data: { conversationId: string; receiverId: number },
  ): void {
    const userId = client.data.userId;
    if (!userId) {
      return;
    }

    const { conversationId, receiverId } = data;

    // 如果接收者在线，通知对方正在输入
    if (this.isUserOnline(receiverId)) {
      this.sendToUser(receiverId, 'user-typing', {
        type: 'USER_TYPING',
        conversationId,
        userId,
        timestamp: new Date(),
      });
      this.logger.log(`用户 ${userId} 正在向用户 ${receiverId} 输入消息`);
    }
  }

  /**
   * 处理用户停止输入事件
   */
  @SubscribeMessage('stop-typing')
  handleStopTyping(
    @ConnectedSocket() client: AuthenticatedSocket,
    @MessageBody() data: { conversationId: string; receiverId: number },
  ): void {
    const userId = client.data.userId;
    if (!userId) {
      return;
    }

    const { conversationId, receiverId } = data;

    // 如果接收者在线，通知对方停止输入
    if (this.isUserOnline(receiverId)) {
      this.sendToUser(receiverId, 'user-stop-typing', {
        type: 'USER_STOP_TYPING',
        conversationId,
        userId,
        timestamp: new Date(),
      });
    }
  }

  /**
   * 发送消息给指定用户
   * @param userId 用户 ID
   * @param event 事件名称
   * @param data 数据
   */
  sendToUser(userId: number, event: string, data: any): boolean {
    const socket = this.onlineUsers.get(userId);
    if (socket && socket.connected) {
      socket.emit(event, data);
      this.logger.log(`已向用户 ${userId} 发送 ${event} 事件`);
      return true;
    }
    this.logger.warn(`用户 ${userId} 不在线或连接已断开`);
    return false;
  }

  /**
   * 广播消息给所有在线用户
   * @param event 事件名称
   * @param data 数据
   */
  broadcast(event: string, data: any): void {
    this.server.emit(event, data);
    this.logger.log(`已广播 ${event} 事件给所有在线用户`);
  }

  /**
   * 检查用户是否在线
   * @param userId 用户 ID
   */
  isUserOnline(userId: number): boolean {
    const socket = this.onlineUsers.get(userId);
    return socket !== undefined && socket.connected;
  }

  /**
   * 获取在线用户数量
   */
  getOnlineUserCount(): number {
    return this.onlineUsers.size;
  }

  /**
   * 获取所有在线用户 ID
   */
  getOnlineUserIds(): number[] {
    return Array.from(this.onlineUsers.keys());
  }

  /**
   * 踢出指定用户（强制断开连接）
   * @param userId 用户 ID
   */
  kickUser(userId: number): boolean {
    const socket = this.onlineUsers.get(userId);
    if (socket) {
      socket.disconnect();
      this.onlineUsers.delete(userId);
      this.logger.log(`已踢出用户 ${userId}`);
      return true;
    }
    return false;
  }
}
